As computers become faster and more powerful, and multiprocessor computers become more common, the use of concurrent programming will grow. Concurrent programs are the result of the desire to have the computer continue useful program execution while a part of the program is unable to make progress due to some delay, such as waiting for a response from a peripheral device like a hard drive. The separate parts of a computer program executing simultaneously (typically known as threads) often access and manipulate the same data located in shared memory space. When two or more threads manipulate a single, shared piece of data, several problems may occur, including race conditions, deadlock, and incorrect results. Threads can be synchronized through good management of shared memory, which can eliminate many of these problems, but writing the code to do so is very difficult even for experienced programmers. Therefore, the benefits of concurrent programming, such as increased efficiency and reduced execution time, are often greatly diminished due to poor management of shared memory.
Several methods have developed for better handling of shared memory. This helps to synchronize multiple threads and ensures that correct results are generated while maximizing the benefits of concurrent programming. Mutual exclusion, or mutex, is one such method. Mutex may take several forms, including disabling interrupts while the portion of the code using the shared data executes, and using flags, locks or semaphores to indicate that shared data is in use. This prevents other threads from manipulating the data.
Shared data may also be handled with transactional memory, a mechanism that allows access to the shared data by all threads, but records every read and write of data in a log. When a thread completes a transaction using the shared data, it verifies with the log that no other thread has made changes to the data while the thread was manipulating it. If the data has not been altered by another thread, the changes are committed. If the shared data has been affected by another thread, the thread may abort the transaction, reverse any changes it has made, and re-execute the transaction from the beginning. Alternatively, when more than one thread has modified shared data and the conflict is detected before changes are committed, a decision may be made about which threads should be aborted and which will be allowed to commit its changes. Various other means and methods of implementing transactional memory are known to those skilled in the art.
While these methods and others can be effective at managing shared memory and data, it is very difficult to employ them properly so that the benefits of concurrent programming are fully realized. Often, failure to skillfully use shared memory management mechanisms results in worse performance or incorrect results. Effective use of concurrent programming requires the programmer to think about concurrency in every aspect of the program and address the many possible side effects of improper shared memory use. Programmers will also want to maximize the efficiency of the concurrent program, which often means minimizing the use of shared data management tools. This may leave significant amounts of data unprotected from concurrent programming problems. Moreover, as programs evolve and are modified during their lifespan, the mechanisms put in place initially to optimize the use of shared data may be modified or may no longer be effective, resulting in a program that does not perform as designed. Writing good concurrent programs that ensure thread synchronization and proper use of shared memory, and maintain those qualities through their lifespan, is a daunting challenge for even the most experienced programmers.